//
// Created by root on 6/22/17.
//

#include "lib.h"
#include <glib/gprintf.h>

/* GError check, free and reset for keyfile read. */
static gboolean check_key_ok(GError **err) {
    if (*err != NULL) {
        g_print("key_file err->code: %s\n", (*err)->message);
        g_error_free(*err);
        *err = NULL;
        return FALSE;
    }
    return TRUE;
}

/* set default values for application & initialize variables */
static void context_set_defaults(FunnyWindow *app) {
    app->window = NULL;     /* initialize struct values */
    app->winwidth = 920;      /* default window width  */
    app->winheight = 740;      /* default window height */
    app->winrestore = FALSE;    /* restore window size */

    app->view = NULL;     /* text_view widget for app */
    app->menubar = NULL;     /* menubar widget */
    app->toolbar = NULL;     /* toolbar widget */
    app->showtoolbar = TRUE;     /* toolbar is visible */
    app->statusbar = NULL;     /* statusbar widget */
    app->cid = 0;        /* app id for statusbar */
    app->tagtable = NULL;     /* tagtable for text_view */
    app->tabstop = 8;        /* number of spaces per tab */
    app->softtab = 4;        /* soft tab stop size */
    app->tabstring = NULL;     /* tabstring for indent */
    app->expandtab = TRUE;     /* insert spaces for tab */
    app->smartbs = TRUE;     /* use smart backspace */
    app->smarthe = TRUE;     /* smart home & end cursor */
    app->kphome = FALSE;    /* previous key press was home */
    app->showtabs = FALSE;    /* display tabulator markers */
    app->dynwrap = TRUE;     /* use dynamic word wrap */
    app->showdwrap = FALSE;    /* use dynamic word wrap */
    app->wraptxtcsr = TRUE;     /* wrap cursor to next line */
    app->pgudmvscsr = TRUE;     /* PgUp/PgDn keys move cursor */
    app->indentwspc = TRUE;     /* indent w/spaces not tabs */
    app->indentmixd = FALSE;    /* Emacs mode indent w/mixed spc/tabs */
    app->indentauto = TRUE;     /* auto-indent on return */
    app->posixeof = TRUE;     /* insure POSIX end of line on save */
    app->trimendws = TRUE;     /* (bug with Quit) remove trailing ws on save */

    //app->langmgr        = gtk_source_language_manager_get_default();
    //app->language       = NULL;
//     app->schememgr      = gtk_source_style_scheme_manager_get_default();
//     app->schemeids      = gtk_source_style_scheme_manager_get_scheme_ids (app->schememgr);
    app->highlight = TRUE;     /* show syntax highlight */
    app->lineno = FALSE;    /* show line numbers (sourceview) */
    app->linehghlt = TRUE;     /* highlight current line */
    app->laststyle = NULL;

    app->filename = NULL;     /* full filename */
    app->fname = NULL;     /* base filename w/o ext */
    app->fext = NULL;     /* filename extension */
    app->fpath = NULL;     /* path components of fname */
    app->modified = FALSE;    /* need save if not 0  */
    app->fsize = 0;        /* resulting file size */
    app->filemode = 0;        /* existing file mode */
    app->fileuid = 0;        /* existing file UID */
    app->filegid = 0;        /* existing file GID */
    /* initial font name */
    app->fontname = g_strdup(DEFAULT_FONT_NAME);

    app->appname = "Funny Editor";   /* app name, e.g. "GTKwrite Text Editor" */
    app->appshort = "Funny";  /* short name, e.g. "GTKwrite" */
    app->comment = g_strdup("// ");

    app->new_pos = NULL;     /* Goto mark, no sep init */

    app->printsettings = NULL;     /* initialize print settings */
    app->printpgsetup = NULL;     /* initialize page setup */
    app->margintop = 0.5;      /* default margins */
    app->marginbottom = 0.5;
    app->marginleft = 1.0;
    app->marginright = 0.5;

    app->cfgdir = NULL;     /* user config dir */
    app->cfgfile = NULL;     /* user config file */
    app->keyfile = NULL;
}

#define READ_BOOLEAN_FROM_KEY_FILE(_group,_key,_var) bv = g_key_file_get_boolean(app->keyfile, _group, _key, &err); \
    if (check_key_ok(&err)) app->_var = bv
#define READ_INTEGER_FROM_KEY_FILE(_group,_key,_var) iv = g_key_file_get_integer(app->keyfile, _group, _key, &err); \
    if (check_key_ok(&err)) app->_var = iv;
#define READ_STRING_FROM_KEY_FILE(_group,_key,_var) sv = g_key_file_get_string(app->keyfile, _group, _key, &err); \
    if (check_key_ok(&err)) app->_var = sv;

/** read saved values from key_file, if it exists. */
static void context_read_keyfile(FunnyWindow *app) {
    get_user_cfgfile(app);

    if (!g_file_test(app->cfgfile, G_FILE_TEST_EXISTS))
        return;

    if (!g_key_file_load_from_file(app->keyfile, app->cfgfile, G_KEY_FILE_NONE, NULL))
        return;

    gchar *sv;
    gboolean bv;
    gint iv;
    gdouble dv;
    GError *err = NULL;

    /** initialize "appearance" values from keyfile */
    READ_BOOLEAN_FROM_KEY_FILE("appearance", "winrestore", winrestore);
    if (app->winrestore) {
        READ_INTEGER_FROM_KEY_FILE("appearance", "winwidth", winwidth);
        READ_INTEGER_FROM_KEY_FILE("appearance", "winheight", winheight);
    }
    READ_BOOLEAN_FROM_KEY_FILE("appearance", "showtoolbar", showtoolbar);
    READ_BOOLEAN_FROM_KEY_FILE("appearance", "showtabs", showtabs);
    READ_BOOLEAN_FROM_KEY_FILE("appearance", "showdwrap", showdwrap);
    //READ_STRING_FROM_KEY_FILE("appearance", "fontname", fontname);
    sv = g_key_file_get_string(app->keyfile, "appearance", "fontname", &err);
    if (check_key_ok(&err)) {
        if (app->fontname) g_free(app->fontname);
        app->fontname = sv;
    }

    /** initialize "editor" values from keyfile */
    READ_INTEGER_FROM_KEY_FILE("editor", "tabstop", tabstop);
    READ_INTEGER_FROM_KEY_FILE("editor", "softtab", softtab);
    READ_STRING_FROM_KEY_FILE("editor", "tabstring", tabstring);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "expandtab", expandtab);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "smartbs", smartbs);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "smarthe", smarthe);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "dynwrap", dynwrap);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "wraptxtcsr", wraptxtcsr);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "pgudmvscsr", pgudmvscsr);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "indentwspc", indentwspc);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "indentmixd", indentmixd);
    READ_BOOLEAN_FROM_KEY_FILE("editor", "indentauto", indentauto);

    /** initialize "cleanup" values from keyfile */
    READ_BOOLEAN_FROM_KEY_FILE("cleanup", "posixeof", posixeof);
    READ_BOOLEAN_FROM_KEY_FILE("cleanup", "trimendws", trimendws);

#ifdef HAVESOURCEVIEW
    /** initialize "sourceview" values from keyfile */
    READ_BOOLEAN_FROM_KEY_FILE("sourceview", "highlight", highlight);
    READ_BOOLEAN_FROM_KEY_FILE("sourceview", "lineno", lineno);
    READ_BOOLEAN_FROM_KEY_FILE("sourceview", "linehghlt", linehghlt);
    if (g_key_file_has_key (app->keyfile, "sourceview", "laststyle", &err)) {
        sv = g_key_file_get_string (app->keyfile, "sourceview", "laststyle", &err);
        if (chk_key_ok (&err)) app->laststyle = sv;
    }
#endif

    /** initialize "print" values from keyfile */
    /*
    dv = g_key_file_get_double(app->keyfile, "print", "margintop", &err);
    if (check_key_ok(&err)) app->margintop = dv;

    dv = g_key_file_get_double(app->keyfile, "print", "marginbottom", &err);
    if (check_key_ok(&err)) app->marginbottom = dv;

    dv = g_key_file_get_double(app->keyfile, "print", "marginleft", &err);
    if (check_key_ok(&err)) app->marginleft = dv;

    dv = g_key_file_get_double(app->keyfile, "print", "marginright", &err);
    if (check_key_ok(&err)) app->marginright = dv;
    */
}

/** write settings to key_file, key_file created if it doesn't exist.
 *  (default location $HOME/.config/gtkwrite/gtkwrite.cfg)
 */
static void context_write_keyfile(FunnyWindow *app) {
    if (!app->keyfile || !app->cfgfile) return;

    if (g_mkdir_with_parents(app->cfgdir, 0755)) {
        g_print("error: directory creation failed '%s'\n", app->cfgdir);
        return;
    }

    g_key_file_set_boolean(app->keyfile, "appearance", "winrestore", app->winrestore);
    g_key_file_set_integer(app->keyfile, "appearance", "winwidth", app->winwidth);
    g_key_file_set_integer(app->keyfile, "appearance", "winheight", app->winheight);
    g_key_file_set_boolean(app->keyfile, "appearance", "showtoolbar", app->showtoolbar);
    g_key_file_set_boolean(app->keyfile, "appearance", "showtabs", app->showtabs);
    g_key_file_set_boolean(app->keyfile, "appearance", "showdwrap", app->showdwrap);
    g_key_file_set_string(app->keyfile, "appearance", "fontname", app->fontname);
    g_key_file_set_integer(app->keyfile, "editor", "tabstop", app->tabstop);
    g_key_file_set_integer(app->keyfile, "editor", "softtab", app->softtab);
    g_key_file_set_string(app->keyfile, "editor", "tabstring", app->tabstring);
    g_key_file_set_boolean(app->keyfile, "editor", "expandtab", app->expandtab);
    g_key_file_set_boolean(app->keyfile, "editor", "smartbs", app->smartbs);
    g_key_file_set_boolean(app->keyfile, "editor", "smarthe", app->smarthe);
    g_key_file_set_boolean(app->keyfile, "editor", "dynwrap", app->dynwrap);
    g_key_file_set_boolean(app->keyfile, "editor", "wraptxtcsr", app->wraptxtcsr);
    g_key_file_set_boolean(app->keyfile, "editor", "pgudmvscsr", app->pgudmvscsr);
    g_key_file_set_boolean(app->keyfile, "editor", "indentwspc", app->indentwspc);
    g_key_file_set_boolean(app->keyfile, "editor", "indentmixd", app->indentmixd);
    g_key_file_set_boolean(app->keyfile, "editor", "indentauto", app->indentauto);
    g_key_file_set_boolean(app->keyfile, "cleanup", "posixeof", app->posixeof);
    g_key_file_set_boolean(app->keyfile, "cleanup", "trimendws", app->trimendws);
#ifdef HAVESOURCEVIEW
    g_key_file_set_boolean (app->keyfile, "sourceview", "highlight", app->highlight);
    g_key_file_set_boolean (app->keyfile, "sourceview", "lineno", app->lineno);
    g_key_file_set_boolean (app->keyfile, "sourceview", "linehghlt", app->linehghlt);
    if (app->laststyle) {
        g_key_file_set_string  (app->keyfile, "sourceview", "laststyle", app->laststyle);
    }
#endif
    g_key_file_set_double(app->keyfile, "print", "margintop", app->margintop);
    g_key_file_set_double(app->keyfile, "print", "marginbottom", app->marginbottom);
    g_key_file_set_double(app->keyfile, "print", "marginleft", app->marginleft);
    g_key_file_set_double(app->keyfile, "print", "marginright", app->marginright);

#ifdef HAVEKEYFILE
    if (!g_key_file_save_to_file (app->keyfile, app->cfgfile, NULL))
        g_print ("error: saving keyfile failed '%s'\n", app->cfgfile);
#else
    GError *err = NULL;
    gchar *kfd;
    gsize len;

    kfd = g_key_file_to_data(app->keyfile, &len, NULL);
    if (!g_file_set_contents(app->cfgfile, kfd, -1, &err)) {
        err_dialog(err->message);
        g_error_free(err);
    }
    if (kfd) g_free(kfd);
#endif

}

/** set default values and read save values from key_file.
 *  (wrapper for functions above)
 */
void context_init(FunnyWindow *app) {
    /* load default values */
    context_set_defaults(app);

    /* create an empty key_file */
    app->keyfile = g_key_file_new();

    /* read key_file values from file
     * (default $HOME/.config/gtkwrite/gtkwrite.cfg)
     */
    //context_read_keyfile(app);

    /* initialize find/replace values */
    //findrep_init(app);
}

/* initialize find/replace variables and values. */
void findrep_init(FunnyWindow *app) {
    app->findrepwin = NULL; /* initialize widgets to NULL */
    app->dialogid = 0;
    app->entryfind = NULL;
    app->entryreplace = NULL;
    app->btnregex = NULL;
    app->btnplace = NULL;
    app->chkregex = NULL;
    app->chkplace = NULL;
    app->chkcase = NULL;
    app->chkwhole = NULL;
    app->chkfrom = NULL;
    app->chkback = NULL;
    app->chkselect = NULL;
    app->chkprompt = NULL;  /* allocate array of pointers */
    app->findtext = g_malloc0(MAXLE * sizeof *(app->findtext));
    app->reptext = g_malloc0(MAXLE * sizeof *(app->reptext));
    app->nfentries = 0;
    app->nrentries = 0;
    app->fmax = MAXLE;
    app->rmax = MAXLE;
    app->optregex = TRUE;  /* initial checkbox states */
    app->optplace = FALSE;
    app->optcase = TRUE;
    app->optwhole = FALSE;
    app->optfrom = FALSE;
    app->optback = FALSE;
    app->optselect = FALSE;
    app->optprompt = FALSE;
    app->findcbchgd = FALSE;
    app->replcbchgd = FALSE;

    app->txtfound = FALSE;
    app->last_pos = NULL;
    app->markfrom = NULL;
    app->selstart = NULL;
    app->selend = NULL;

    if (!(app->findtext && app->reptext)) {
        // err_dialog ("findrep_init()\nvirtual memory exhausted.");
        g_print("findrep_init()\nvirtual memory exhausted.");
        // findrep_destroy (app);
        // context_destroy (app);
        gtk_main_quit();
    }
}

/** free allocated memory on exit */
void context_destroy(FunnyWindow *app) {
    /* save settings to keyfile */
    //context_write_keyfile(app);

    /* free allocated struct values */
    app_free_filename(app);
    if (app->fontname) g_free(app->fontname);

    // if (app->appname) g_free (app->appname);
    // if (app->appshort) g_free (app->appshort);

    if (app->tabstring) g_free(app->tabstring);

    if (app->cfgdir) g_free(app->cfgdir);
    if (app->cfgfile) g_free(app->cfgfile);

    if (app->keyfile) g_key_file_free(app->keyfile);

    /* free find/replace GList memory */
    //findrep_destroy(app);
}

/** free memory allocated to find/replace on exit */
void findrep_destroy(FunnyWindow *app) {
    guint i;

    /* free combobox lists */
    for (i = 0; i < app->nfentries; i++) g_free(app->findtext[i]);
    g_free(app->findtext);

    for (i = 0; i < app->nrentries; i++) g_free(app->reptext[i]);
    g_free(app->reptext);

}

/** app_free_filename, free all filename components. */
void app_free_filename(FunnyWindow *app) {
    if (app->filename) g_free(app->filename);
    if (app->fname) g_free(app->fname);
    if (app->fext) g_free(app->fext);
    if (app->fpath) g_free(app->fpath);
    app->filename = NULL;
    app->fname = NULL;
    app->fext = NULL;
    app->fpath = NULL;
}

/** convert uri to filename.
 *  returns pointer to beginning of filename within uri.
 */
gchar *uri_to_filename(const gchar *uri) {
    if (!uri || !*uri) return NULL;

    gchar *p = (gchar *) uri;
    gsize len = g_strlen(uri);

    if (len < 4) return NULL;

    for (p += 2; *p; p++) {
        if (*(p - 2) == '/' && *(p - 1) == '/' && *p == '/')
            return p;
    }

    return NULL;
}

/** form key_file filename from config_dir and defines */
char *get_user_cfgfile(FunnyWindow *app) {
    gchar *cfgdir;

    if ((cfgdir = (gchar *) g_get_user_config_dir())) {
        app->cfgdir = g_strdup_printf("%s/%s", cfgdir, "gtkwrite");
        if (!app->cfgdir)
            return NULL;
        return (app->cfgfile = g_strdup_printf("%s/%s", app->cfgdir, "gtkwrite.cfg"));
    }

    return NULL;
}

int editor_start(int argc, char **argv) {
    FunnyWindow *app = NULL;            /* replaced GtkWidget *window */
    app = g_slice_new (FunnyWindow);    /* allocate mem for struct    */
    context_init(app);                 /* initialize struct values   */

    // gtk_init (&argc, &argv);        /* terminates if can't initialize */
    if (!gtk_init_check(&argc, &argv)) {   /* same but doesn't exit */
        g_fprintf(stderr, "error: gtk_init failed.\n");
        return 1;
    }

    if (!create_window(app)) {     // create the main window
        g_fprintf(stderr, "error: create_window failed.\n");
        return 1;
    }

    if (argc > 1) {  // TODO: implement getopt
        app->filename = g_strdup(argv[1]);
        split_fname(app);
        buffer_insert_file(app, NULL);
    }

    gtk_main();

    context_destroy(app);          /* free allocated members   */
    g_slice_free (FunnyWindow, app);    /* free struct memory       */

    return 0;
}

int main(int argc, char **argv) {
    return editor_start(argc, argv);
}

